#include "riscv_encoding.h"
# save all General-Purpose(GP) registers to context
# struct context *base = &ctx_task;
# base->ra = ra;
# ......
.macro reg_save base
	sw ra, 0(\base)
	sw sp, 4(\base)
	sw gp, 8(\base)
	sw tp, 12(\base)
	sw t0, 16(\base)
	sw t1, 20(\base)
	sw t2, 24(\base)
	sw s0, 28(\base)
	sw s1, 32(\base)
	sw a0, 36(\base)
	sw a1, 40(\base)
	sw a2, 44(\base)
	sw a3, 48(\base)
	sw a4, 52(\base)
	sw a5, 56(\base)
	sw a6, 60(\base)
	sw a7, 64(\base)
	sw s2, 68(\base)
	sw s3, 72(\base)
	sw s4, 76(\base)
	sw s5, 80(\base)
	sw s6, 84(\base)
	sw s7, 88(\base)
	sw s8, 92(\base)
	sw s9, 96(\base)
	sw s10, 100(\base)
	sw s11, 104(\base)
	sw t3, 108(\base)
	sw t4, 112(\base)
	sw t5, 116(\base)
	# we don't save t6 here, due to we have used
	# it as base, we have to save t6 in an extra step
	# outside of reg_save
.endm

# restore all General-Purpose(GP) registers from the context
# struct context *base = &ctx_task;
# ra = base->ra;
# ......
.macro reg_restore base
	lw ra, 0(\base)
	lw sp, 4(\base)
	lw gp, 8(\base)
	lw tp, 12(\base)
	lw t0, 16(\base)
	lw t1, 20(\base)
	lw t2, 24(\base)
	lw s0, 28(\base)
	lw s1, 32(\base)
	lw a0, 36(\base)
	lw a1, 40(\base)
	lw a2, 44(\base)
	lw a3, 48(\base)
	lw a4, 52(\base)
	lw a5, 56(\base)
	lw a6, 60(\base)
	lw a7, 64(\base)
	lw s2, 68(\base)
	lw s3, 72(\base)
	lw s4, 76(\base)
	lw s5, 80(\base)
	lw s6, 84(\base)
	lw s7, 88(\base)
	lw s8, 92(\base)
	lw s9, 96(\base)
	lw s10, 100(\base)
	lw s11, 104(\base)
	lw t3, 108(\base)
	lw t4, 112(\base)
	lw t5, 116(\base)
	lw t6, 120(\base)
.endm

# Something to note about save/restore:
# - We use CSR_MSCRATCH to hold a pointer to context of previous task
# - We use t6 as the 'base' for reg_save/reg_restore, because it is the
#   very bottom register (x31) and would not be overwritten during loading.

.text

.global w_mscratch
.align 4
w_mscratch:
	csrw CSR_MSCRATCH, a0
	ret

# interrupts and exceptions while in machine mode come here.
#.globl eclic_mtip_handler
# the trap vector base address must always be aligned on a 4-byte boundary
#.align 4
#eclic_mtip_handler:

.global eclic_mtip_handler
.align 4
eclic_mtip_handler:
	# save context(registers).
	csrr    t6, CSR_MSCRATCH
	reg_save t6

	# save mepc to context of current task
	csrr    a0, mepc
	sw  a0, 124(t6)

	call timer_handler

	mret


# void switch_to(struct context *next);
# a0: pointer to the context of the next task
.globl switch_to
.align 4
switch_to:
	csrw	CSR_MSCRATCH, a0
	# set mepc to the pc of the next task
	lw	a1, 124(a0)
	csrw	mepc, a1
	# Restore all GP registers
	# Use t6 to point to the context of the new task
	mv	t6, a0

	# When running on the MCU gd32vf103, when the mstatus register mie is turned on,
	# directly using mret without interruption will cause mie to be turned off.
	# So when switch_to is called for the first time, ret is used here instead of mret.
	#
	# How do I know that switch_to is called for the first time?
	# When a trap occurs, the mie in the mstatus register will be assigned to mpie,
	# and then the mie value will be cleared to 0. These are hardware behaviors.
	# We know that no trap occurs when switch_to is called for the first time,
	# and switch_to is called in timer interrupts afterwards.
	# So here we judge whether switch_to is called for the first time according to mie in mstatus.
	# Of course, the safest way is to determine whether the interrupt type is a timer interrupt,
	# we will not try.

	csrr a0, CSR_MSTATUS
	and a0, a0, MSTATUS_MIE

	beqz a0, 1f
	reg_restore t6
	csrr ra, mepc
	ret
1:
	# in trap
	reg_restore t6
	# Do actual context switching.
	# Notice this will enable global interrupt
	mret

.end

